package data;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.math3.random.RandomDataGenerator;
import util.*;

import java.math.BigDecimal;
import java.util.*;

/*
 * 分段类
 * Sample:样本号
 * chromosome:染色体
 * start:起始位点
 * end:终止位点
 * countsOfProbes:分段上探针总数
 * log_ratio:值:(log(X/2))，每一个分段
 * log_ratio_everyPoint:每个点的拷贝数值
 *
 * */
public class Segments {
    private String[] sample;
    private String[] chromosome;
    private int[] start;
    private int[] end;
    private int[] countsOfProbes;
    private double[] log_ratio;
    private double[][] log_ratio_everyPoint;

    public double[][] getLog_ratio_everyPoint() {
        return log_ratio_everyPoint;
    }

    public void setLog_ratio_everyPoint(double[][] log_ratio_everyPoint) {
        this.log_ratio_everyPoint = log_ratio_everyPoint;
    }

    public String[] getSample() {
        return sample;
    }

    public void setSample(String[] sample) {
        this.sample = sample;
    }

    public String[] getChromosome() {
        return chromosome;
    }

    public void setChromosome(String[] chromosome) {
        this.chromosome = chromosome;
    }

    public int[] getStart() {
        return start;
    }

    public void setStart(int[] start) {
        this.start = start;
    }

    public int[] getEnd() {
        return end;
    }

    public void setEnd(int[] end) {
        this.end = end;
    }

    public int[] getCountsOfProbes() {
        return countsOfProbes;
    }

    public void setCountsOfProbes(int[] countsOfProbes) {
        this.countsOfProbes = countsOfProbes;
    }

    public double[] getLog_ratio() {
        return log_ratio;
    }

    public void setLog_ratio(double[] log_ratio) {
        this.log_ratio = log_ratio;
    }

    /*
     * 将文件取出来的值赋值
     * filePath:文件路径
     * symbol:分割符
     * col_title:是否具有列名
     *
     * */
    public void assign(String filePath, String symbol, boolean col_title){
        //list:提取的数据（每一个元素是一行数据，未被分割）
        List list = ImportFile.readFile(filePath);

        int row_length = col_title?list.size()-1:list.size();
        int column_length = list.get(0).toString().split(symbol).length;
        String[] lineData = new String[column_length];

        sample = new String[row_length];
        chromosome = new String[row_length];
        start = new int[row_length];
        end = new int[row_length];
        countsOfProbes = new int[row_length];
        log_ratio = new double[row_length];

        for (int i= 0 ;i<row_length;i++){
            if(col_title==false){
                lineData = list.get(i).toString().split(symbol);
            }else {
                lineData = list.get(i+1).toString().split(symbol);
            }
            int j = 0;
            this.sample[i] = lineData[j++];
            this.chromosome[i] = lineData[j++];
            this.start[i] = Integer.valueOf(lineData[j++]);
            this.end[i] = Integer.valueOf(lineData[j++]);
            this.countsOfProbes[i] = Integer.valueOf(lineData[j++]);
            this.log_ratio[i] = Double.valueOf(lineData[j]);
        }
    }


    /*
     * 模拟数据（仿真数据）
     * basicNumber:基础流水号
     * samplesNumber:样本数量
     * passengers:乘客基因
     * markers:生成的标记
     * windows:窗口（驱动基因）
     *
     * */
    public void simulate(String basicNumber,int samplesNumber,
                         Passengers passengers, Markers markers, Window windows){
        int[] w_midPos = windows.getMidPos(); //窗口中点
        int[] w_width = windows.getWidth(); //窗口长度
        double[] w_LRRVal = windows.getValue(); //窗口值
        int p_number = passengers.getNumber(); //乘客数量
        int p_width = passengers.getWidth(); //乘客数目
        double p_value = passengers.getValue(); //乘客值
        int m_number = markers.getM_detail().totalNumber;
        int[] m_probesPerChrom = markers.getM_detail().probesPerChrom;
        int[] m_position = markers.getPosition();
        int[] m_chromStartPoint = markers.getM_detail().start;
        int[] m_chromEndPoint = markers.getM_detail().end;
        //double[][] m_log_ratio = markers.getM_detail().log_ratio;
        //噪声参数
        double alpha = windows.getAlpha();
        double beta = windows.getBeta();
        double sigma = windows.getSigma();

        //实际选择窗口中心点时，因为数组从0开始，所以用户选择的位置需要减一
        for (int i=0;i<w_midPos.length;i++){
            w_midPos[i]--;
        }


        int total_segNumber = 0; //计算出所有的分段数
        List<List> segment_allSamples = new ArrayList<List>();
        this.log_ratio_everyPoint = new double[samplesNumber][m_number];  //所有样本的每个点的拷贝数值

        for (int i=0;i<samplesNumber;i++){
            segment_allSamples.add(generateSingleSample(basicNumber,i+1,m_probesPerChrom,m_position,m_chromStartPoint,
                    m_chromEndPoint,m_number,p_number,p_width,p_value,w_midPos,w_width,w_LRRVal,alpha,beta,sigma));
            total_segNumber += ( (List)segment_allSamples.get(i).get(0) ).size();
        }


        //赋予Segment参数值
        sample = new String[total_segNumber];
        chromosome = new String[total_segNumber];
        start = new int[total_segNumber];
        end = new int[total_segNumber];
        countsOfProbes = new int[total_segNumber];
        log_ratio = new double[total_segNumber];

        List segmentPerSample;
        int order = 0;
        List serialNumberPerSamples;
        List chromosomePerSamples;
        List startPointPerSamples;
        List endPointPerSamples;
        List countsOfProbesPerSamples;
        List log_ratioPerSamples;
        int segNumberPerSample;

        for (int i=0;i<samplesNumber;i++){
            segmentPerSample = segment_allSamples.get(i);
            serialNumberPerSamples = (List)segmentPerSample.get(0);
            chromosomePerSamples = (List)segmentPerSample.get(1);
            startPointPerSamples = (List)segmentPerSample.get(2);
            endPointPerSamples = (List)segmentPerSample.get(3);
            countsOfProbesPerSamples = (List)segmentPerSample.get(4);
            log_ratioPerSamples = (List)segmentPerSample.get(5);

            segNumberPerSample = serialNumberPerSamples.size();

            for (int j=0;j<segNumberPerSample;j++){
                sample[order] = serialNumberPerSamples.get(j).toString();
                chromosome[order] = chromosomePerSamples.get(j).toString();
                start[order] = (int)startPointPerSamples.get(j);
                end[order] = (int)endPointPerSamples.get(j);
                countsOfProbes[order] = (int)countsOfProbesPerSamples.get(j);
                log_ratio[order] = (double)log_ratioPerSamples.get(j);
                order++;
            }
        }
    }


    /*
     * 模拟数据（来源真实数据）(建立一个abberation list，以“抽奖”方式取出相应值，并覆盖相应分段)
     * basicNumber:基础流水号
     * samplesNumber:样本数量
     * lotteryNumber:从AbberationList取几次
     * abbeartionList:真实数据组成的一个乘客表
     * markers:生成的标记
     * windows:窗口（驱动基因）
     *
     * */
    public void simulateFromRealData(String basicNumber,int samplesNumber,
                                      Abberation abberation, Markers markers, Window windows){
        int[] w_midPos = windows.getMidPos(); //窗口中点
        int[] w_width = windows.getWidth(); //窗口长度
        double[] w_LRRVal = windows.getValue(); //窗口值
        int a_lotterytNumber = abberation.getLotteryNumber();
        List a_list = abberation.getAbberationList();
        int m_number = markers.getM_detail().totalNumber;
        int[] m_probesPerChrom = markers.getM_detail().probesPerChrom;
        int[] m_position = markers.getPosition();
        int[] m_chromStartPoint = markers.getM_detail().start;
        int[] m_chromEndPoint = markers.getM_detail().end;
        //噪声参数
        double alpha = windows.getAlpha();
        double beta = windows.getBeta();
        double sigma = windows.getSigma();

        //实际选择窗口中心点时，因为数组从0开始，所以用户选择的位置需要减一
        for (int i=0;i<w_midPos.length;i++){
            w_midPos[i]--;
        }


        int total_segNumber = 0; //计算出所有的分段数
        List<List> segment_allSamples = new ArrayList<List>();
        this.log_ratio_everyPoint = new double[samplesNumber][m_number];  //所有样本的每个点的拷贝数值

        for (int i=0;i<samplesNumber;i++){
            segment_allSamples.add(generateSingleSampleFromRealData(basicNumber,i+1,m_probesPerChrom,m_position,m_chromStartPoint,m_chromEndPoint,
                    m_number,a_lotterytNumber,a_list,w_midPos,w_width,w_LRRVal,alpha,beta,sigma));
            total_segNumber += ( (String[])segment_allSamples.get(i).get(0) ).length;
        }


        //赋予Segment参数值
        sample = new String[total_segNumber];
        chromosome = new String[total_segNumber];
        start = new int[total_segNumber];
        end = new int[total_segNumber];
        countsOfProbes = new int[total_segNumber];
        log_ratio = new double[total_segNumber];

        int order = 0;
        List segmentPerSample;
        String[] serialNumberPerSamples;
        String[] chromosomePerSamples;
        int[] startPointPerSamples;
        int[] endPointPerSamples;
        int[] countsOfProbesPerSamples;
        double[] log_ratioPerSamples;

        int segNumberPerSample;



        for (int i=0;i<samplesNumber;i++){
            segmentPerSample = segment_allSamples.get(i);
            serialNumberPerSamples = (String[])segmentPerSample.get(0);
            chromosomePerSamples = (String[])segmentPerSample.get(1);
            startPointPerSamples = (int[])segmentPerSample.get(2);
            endPointPerSamples = (int[])segmentPerSample.get(3);
            countsOfProbesPerSamples = (int[])segmentPerSample.get(4);
            log_ratioPerSamples = (double[])segmentPerSample.get(5);

            segNumberPerSample = serialNumberPerSamples.length;

            for (int j=0;j<segNumberPerSample;j++){
                sample[order] = serialNumberPerSamples[j];
                chromosome[order] = chromosomePerSamples[j];
                start[order] = startPointPerSamples[j];
                end[order] = endPointPerSamples[j];
                countsOfProbes[order] = countsOfProbesPerSamples[j];
                log_ratio[order] = Calculate.chooseScale(log_ratioPerSamples[j],6);
                order++;
            }
        }
    }


    /*
     * 产生单个样本(sample)
     * 【最多不超过23种染色体，来源于"仿真数据"】
     * basicNumber:基础流水号
     * sampleOrder:样本号
     * probesPerChrom:每种染色体占有的探针数
     * markersPosition:染色体位置集
     * chromStartPoint:某种染色体的起始点集
     * chromEndPoint:某种染色体的终止点集
     * markersNumber:标记总数
     * passengersNumber:乘客数量
     * passengersWidth:乘客基因宽度
     * passengersValue:乘客值
     * windowMidPos:窗口中心点
     * windowWidth:窗口范围
     * windowLRRVal:窗口值
     * sigma:σ (用于强度噪声)  用于数据的取值
     * alpha:α（用于空间噪声）  窗口大小的乘性系数
     * beta:β （用于空间噪声）  随机移动位点中值
     *
     * */
    private List generateSingleSample(String basicNumber, int sampleOrder, int[] probesPerChrom,int[] markersPosition,
                                          int[] chromStartPoint, int[] chromEndPoint, int markersNumber, int passengersNumber,
                                          int passengersWidth, double passengersValue, int[] windowMidPos, int[] windowWidth,
                                          double[] windowValue,double alpha,double beta,double sigma){

        //流水号
        String serial_number;
        serial_number = String.valueOf(sampleOrder);
        serial_number = basicNumber.substring(0, basicNumber.length() - serial_number.length()) + serial_number; //将序号和流水基础号组合

        //添加passengers
        double[] log_ratio_everyPoint = new double[markersNumber];

        if (passengersWidth!=0 && passengersNumber!=0 && passengersValue!=0){
            int passengerStart;
            Random rd=new Random();
            for(int i=0;i<passengersNumber;i++){
                passengerStart=rd.nextInt(markersNumber-passengersWidth);
                for(int j=passengerStart;j<passengerStart+passengersWidth;j++){
                    log_ratio_everyPoint[j] = passengersValue;
                }
            }
        }


        //添加drivers
        RandomDataGenerator rdg = new RandomDataGenerator();
        int driversNumber = windowMidPos.length;  //驱动窗口的数量
        int midPosAddNoise;  //加了噪声的窗口中心位点
        int widthAddNoise;  //加了噪声的窗口宽度
        double valueAddNoise;  //加了噪声的窗口值(整体波动)
        int driverStart; //驱动起始点
        int driverEnd; //驱动起始点
        for (int i=0;i<driversNumber;i++){
            midPosAddNoise = (int) (windowMidPos[i] + windowWidth[i] * rdg.nextGaussian(0, beta));
            widthAddNoise = (int) (windowWidth[i] + rdg.nextGaussian(1, alpha * windowWidth[i]));
            valueAddNoise = (windowValue[i] + rdg.nextGaussian(0, sigma));

            //考虑了因为加入噪声波动而超出边界的情况
            driverStart = midPosAddNoise-widthAddNoise/2<=0 ? 0 : midPosAddNoise-widthAddNoise/2;
            driverEnd = midPosAddNoise+widthAddNoise/2>= markersNumber? markersNumber : midPosAddNoise+widthAddNoise/2;

            for(int j=driverStart;j<driverEnd;j++){
                log_ratio_everyPoint[j]=valueAddNoise;
            }
        }

        this.log_ratio_everyPoint[sampleOrder-1] = log_ratio_everyPoint;

        //分段
        List Segment = this.segmentPerSample(serial_number,log_ratio_everyPoint,markersPosition,
                markersNumber,chromStartPoint,chromEndPoint,probesPerChrom);


        return Segment;
    }


    /*
     * 产生单个样本
     * 【最多不超过23种染色体，来源于"真实数据"】
     * basicNumber:基础流水号
     * sampleOrder:样本号
     * probesPerChrom:每种染色体占有的探针数
     * markersPosition:染色体位置集
     * chromStartPoint:某种染色体的起始点集
     * chromEndPoint:某种染色体的终止点集
     * markersNumber:标记总数
     * lotteryNumber:抽奖次数
     * abberationList:畸变列表（来源于真实数据）
     * windowMidPos:窗口中心点
     * windowWidth:窗口范围
     * windowLRRVal:窗口值
     * sigma:σ (用于强度噪声)  用于数据的取值
     * alpha:α（用于空间噪声）  窗口大小的乘性系数
     * beta:β （用于空间噪声）  随机移动位点中值
     *
     * */
    private List generateSingleSampleFromRealData(String basicNumber, int sampleOrder, int[] probesPerChrom, int[] markersPosition,
                                                  int[] chromStartPoint, int[] chromEndPoint,int markersNumber, int lotteryNumber,
                                                  List abberationList, int[] windowMidPos, int[] windowWidth, double[] windowValue,
                                                  double alpha,double beta,double sigma){

        //流水号
        String serial_number;
        serial_number = String.valueOf(sampleOrder);
        serial_number = basicNumber.substring(0, basicNumber.length() - serial_number.length()) + serial_number; //将序号和流水基础号组合

        //添加passengers
        double[] log_ratio_everyPoint = new double[markersNumber];

        int passengerMidPos;
        int passengerWidth;
        double passengerValue;
        int passengerStart;
        int passengerEnd;
        Passengers abbe; //抽奖得出的一个乘客畸变
        Random rd=new Random();

        for (int k=0;k<lotteryNumber;k++){
            abbe = Abberation.lottery(abberationList);
            if (abbe.isFlag() == true){
                //若flag是true，则将染色体整个变为某一个乘客值
                Arrays.fill(log_ratio_everyPoint, abbe.getValue());
            }else {
                //若flag是false，则将将这段畸变的中心点找一个随机位置覆盖，若值超出，则减去超出的部分
                passengerMidPos=rd.nextInt(markersNumber);
                passengerWidth=Math.round(abbe.getWidth()/2);
                passengerValue=abbe.getValue();

                passengerStart = passengerMidPos - passengerWidth >= 0? passengerMidPos - passengerWidth : 0;
                passengerEnd = passengerMidPos + passengerWidth <= markersNumber ? passengerMidPos + passengerWidth : markersNumber;

                for(int j=passengerStart;j<passengerEnd;j++){
                    log_ratio_everyPoint[j] = passengerValue;
                }
            }
        }


        //添加drivers
        RandomDataGenerator rdg = new RandomDataGenerator();
        int driversNumber = windowMidPos.length;  //驱动窗口的数量
        int midPosAddNoise;  //加了噪声的窗口中心位点
        int widthAddNoise;  //加了噪声的窗口宽度
        double valueAddNoise;  //加了噪声的窗口值(整体波动)
        int driverStart; //驱动起始点
        int driverEnd; //驱动起始点
        for (int i=0;i<driversNumber;i++){
            midPosAddNoise = (int) (windowMidPos[i] + windowWidth[i] * rdg.nextGaussian(0, beta));
            widthAddNoise = (int) (windowWidth[i] + rdg.nextGaussian(1, alpha * windowWidth[i]));
            valueAddNoise = (windowValue[i] + rdg.nextGaussian(0, sigma));

            //考虑了因为加入噪声波动而超出边界的情况
            driverStart = midPosAddNoise-widthAddNoise/2<=0 ? 0 : midPosAddNoise-widthAddNoise/2;
            driverEnd = midPosAddNoise+widthAddNoise/2>= markersNumber? markersNumber : midPosAddNoise+widthAddNoise/2;

            for(int j=driverStart;j<driverEnd;j++){
                log_ratio_everyPoint[j]=valueAddNoise;
            }
        }

        this.log_ratio_everyPoint[sampleOrder-1] = log_ratio_everyPoint;

        //赋值
        List Segment = segmentPerSample(serial_number,log_ratio_everyPoint,markersPosition,
                markersNumber,chromStartPoint,chromEndPoint,probesPerChrom);


        return Segment;
    }




    /*
     * 对单个样本(sample)产生指定分段（分段，先统计内部的分段数目（不包括头尾），然后再将每种染色体头尾加入考虑）
     * 【当前一个染色体的尾和后一个染色体的头同时与原有断点重叠，才不用将这两个点计入为分段点】
     * serial_number:流水号
     * log_ratio_everyPoint:每一个探针（markers）位点的拷贝数值
     * markersPosition:探针的真实位置集
     * markersNumber:探针总数
     * chromStartPoint:某种染色体的起始点集
     * chromEndPoint:某种染色体的终止点集
     * probesPerChrom:每一种染色体占有的探针数
     *
     * */
    private List segmentPerSample(String serial_number,double[] log_ratio_everyPoint,int[] markersPosition,
                                  int markersNumber, int[] chromStartPoint, int[] chromEndPoint, int[] probesPerChrom){

        List<String> sample_perSegment = new ArrayList<String>();
        List<String> chromosome_perSegment = new ArrayList<String>();
        List<Integer> startPoint_perSegment = new ArrayList<Integer>();
        List<Integer> endPoint_perSegment = new ArrayList<Integer>();
        List<Integer> countsOfProbes_perSegment = new ArrayList<Integer>();
        List<Double> log_ratio_perSegment = new ArrayList<Double>();

        List logSegPoint = new ArrayList();

        //先统计分段点位置（包含前后两个点）
        double temp = log_ratio_everyPoint[0];

        for (int i=0;i<markersNumber;i++){ //除了首尾之外的内部分段点
            if (log_ratio_everyPoint[i] != temp) {
                //通过前后断点判断分段
                logSegPoint.add(i - 1); //前点
                logSegPoint.add(i); //后点
                temp = log_ratio_everyPoint[i];
            }
        }


        //当前一个染色体的尾和后一个染色体的头同时与原有断点重叠，才不用将这两个点计入
        int chromosomeNumber = probesPerChrom.length;
        List finalSegPoint = new ArrayList();

        for (int i=0;i<chromosomeNumber;i++){
            //判断染色体起点
            if (logSegPoint.contains(chromStartPoint[i])==false){
                //若原始分段不包含某个染色体头点
                finalSegPoint.add(chromStartPoint[i]);
            }else {
                //若原始分段包含某个染色体头点
                if (i==0){
                    finalSegPoint.add(chromStartPoint[i]);
                }else {
                    if (logSegPoint.contains(chromEndPoint[i-1])==false){
                        finalSegPoint.add(chromStartPoint[i]);
                    }
                }
            }

            //判断染色体尾点
            if (logSegPoint.contains(chromEndPoint[i])==false){
                //若原始分段不包含某个染色体尾点
                finalSegPoint.add(chromEndPoint[i]);
            }else {
                //若原始分段包含某个染色体尾点
                if (i!=chromosomeNumber-1){
                    if (logSegPoint.contains(chromStartPoint[i+1])==false){
                        //logSegPoint.add(chromStartPoint[i+1]);
                        finalSegPoint.add(chromEndPoint[i]);
                    }
                }else { //最后一个点，不太确定
                    finalSegPoint.add(chromEndPoint[i]);
                }
            }
        }

        for (int i=0;i<logSegPoint.size();i++){
            finalSegPoint.add(logSegPoint.get(i));
        }

        Collections.sort(finalSegPoint);  //排序

        //赋值
        for (int i=0;i<finalSegPoint.size();i++){

            if (i%2==0){ //头

                for (int j=0;j<chromosomeNumber;j++){
                    if (chromStartPoint[j]<=(int)finalSegPoint.get(i) && chromEndPoint[j]>=(int)finalSegPoint.get(i)){
                        if (j!=22){
                            chromosome_perSegment.add(String.valueOf(j+1));
                        }else {
                            chromosome_perSegment.add("X");
                        }
                        break;
                    }
                }

                sample_perSegment.add(serial_number);
                startPoint_perSegment.add(markersPosition[(int)finalSegPoint.get(i)]);
            }else { //尾
                endPoint_perSegment.add(markersPosition[(int)finalSegPoint.get(i)]);
                countsOfProbes_perSegment.add((int)finalSegPoint.get(i)-(int)finalSegPoint.get(i-1)+1);
                log_ratio_perSegment.add(log_ratio_everyPoint[(int)finalSegPoint.get(i)]);
            }
        }


        List resultSegment = new ArrayList<>();

        //赋值
        resultSegment.add(sample_perSegment);
        resultSegment.add(chromosome_perSegment);
        resultSegment.add(startPoint_perSegment);
        resultSegment.add(endPoint_perSegment);
        resultSegment.add(countsOfProbes_perSegment);
        resultSegment.add(log_ratio_perSegment);

        return resultSegment;
    }
}